﻿using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using UnityEngine;

namespace Simulation.Framework.Editor
{
    /// <summary>
    /// Panel 代码解析
    /// </summary>
    public class PanelCodeProvider : CSharpCodeProvider
    {
        private List<Block> usingPL = new List<Block>();
        private List<Code> nameSpacePL = new List<Code>();
        private List<Code> classPL = new List<Code>();
        private List<Code> methodPL = new List<Code>();
        private List<Code> propertyPL = new List<Code>();

        public override CodeCompileUnit Parse(TextReader codeStream)
        {
            if (codeStream == null) return null;

            usingPL.Clear();
            nameSpacePL.Clear();
            classPL.Clear();
            methodPL.Clear();
            propertyPL.Clear();

            CodeCompileUnit codeCompileUnit = new CodeCompileUnit();

            string line, script = string.Empty;
            int length = 0;

            List<int> l1 = new List<int>();
            List<int> l2 = new List<int>();

            List<int> l3 = new List<int>();
            List<int> l4 = new List<int>();

            Block nameSpaceHead = new Block();
            Block classHead = new Block();
            Block methodHead = new Block();

            while ((line = codeStream.ReadLine()) != null)
            {
                script += line;

                // using
                if (ContainsAllStrItem(line, GetUsingKeys()))
                {
                    Block body = new Block();
                    body.start = length;
                    body.end = length + line.Length;
                    body.script = line;
                    usingPL.Add(body);
                }

                // 命名空间头
                if (ContainsAllStrItem(line, GetNameSpaceKeys()))
                {
                    int index = line.IndexOf("{");
                    int end = index > 0 ? length + index : length + line.Length;
                    nameSpaceHead.start = length;
                    nameSpaceHead.end = end;
                    nameSpaceHead.script = line.Substring(0,end - length + 1);
                }

                // 类头
                if (ContainsAllStrItem(line, GetClassKeys()))
                {
                    int index = line.IndexOf("{");
                    int end = index > 0 ? length + index : length + line.Length;
                    classHead.start = length;
                    classHead.end = end;
                    classHead.script = line.Substring(0, end - length + 1);
                }

                for (int i = 0; i < line.Length; i++)
                {
                    char c = line[i];
                    // 如果是注释或者在字符串中，不做处理
                    if (IsString(line, c.ToString()) || IsAnnotation(line, c.ToString()))
                        continue;
                    int index = length + i;

                    // 小括号 左
                    if (c == '(')
                    {
                        // 不在方法内
                        if (l1.Count > 2) continue;
                        l3.Add(length);
                    }

                    // 小括号 右
                    if (c == ')')
                    {
                        // 不在方法内
                        if (l1.Count > 2) continue;
                        l4.Add(index);
                        // 方法头
                        methodHead.start = l3[0];
                        methodHead.end = l4[0];
                        methodHead.script = script.Substring(l3[0], l4[0] - l3[0] + 1);
                        l3.RemoveAt(0);
                        l4.RemoveAt(0);
                    }

                    // 大括号左
                    if (c == '{')
                        l1.Add(index);

                    // 大括号右
                    if (c == '}')
                    {
                        l2.Add(index);

                        // 属性
                        if (ContainsAllStrItem(line, GetPropertyKeys()))
                        {
                            int indexof = line.IndexOf('{');

                            Block head = new Block();
                            head.start = length;
                            head.end = length + indexof;
                            head.script = line.Substring(0, indexof);

                            Block body = new Block();
                            body.start = indexof;
                            body.end = length + line.Length;
                            body.script = line.Substring(indexof, line.Length - indexof);
                            propertyPL.Add(new Code(head,body));

                            l1.RemoveAt(2);
                            l2.Remove(index);
                        }
                        // 方法
                        else if (l2.Count == l1.Count - 2)
                        {
                            Block body = new Block();
                            body.start = l1[2];
                            body.end = index;
                            body.script = script.Substring(l1[2], index - l1[2] + 1);

                            Code program = new Code(methodHead, body);

                            methodPL.Add(program);
                            l1.RemoveAt(2);
                            l2.Remove(index);
                        }
                        // 类
                        else if (l2.Count == l1.Count - 1)
                        {
                            Block body = new Block();
                            body.start = l1[1];
                            body.end = index;
                            body.script = script.Substring(l1[1], index - l1[1] + 1);

                            classPL.Add(new Code(classHead, body));
                            l1.RemoveAt(1);
                            l2.Remove(index);
                        }
                        // 命名空间
                        else if (l2.Count == l1.Count)
                        {
                            Block body = new Block();
                            body.start = l1[0];
                            body.end = index;
                            body.script = script.Substring(l1[0], index - l1[0] + 1);

                            nameSpacePL.Add(new Code(nameSpaceHead, body));
                            l1.RemoveAt(0);
                            l2.Remove(index);
                        }
                    }
                }

                length += line.Length;
            }

            // 命名空间
            CreateNamespace(codeCompileUnit);
            // 类
            CreateClass(codeCompileUnit.Namespaces[0]);
            // 方法
            CreateMethod(codeCompileUnit.Namespaces[0].Types[0]);

            codeStream.Close();
            codeStream.Dispose();
            return codeCompileUnit;
        }

        #region get keys

        /// <summary>
        /// using 行关键字
        /// </summary>
        /// <returns></returns>
        private string[] GetUsingKeys()
        {
            return new string[] { "using ", ";" };
        }

        /// <summary>
        /// 命名空间行关键字
        /// </summary>
        /// <returns></returns>
        private string[] GetNameSpaceKeys()
        {
            return new string[] { "namespace" };
        }

        /// <summary>
        /// 类行关键字
        /// </summary>
        /// <returns></returns>
        private string[] GetClassKeys()
        {
            return new string[] { "public", "partial", "class", ":", "PanelBase" };
        }

        /// <summary>
        /// 属性行关键字
        /// </summary>
        /// <returns></returns>
        private string[] GetPropertyKeys()
        {
            return GetPropertyKeys(null);
        }

        /// <summary>
        /// 指定属性行关键字
        /// </summary>
        /// <returns></returns>
        private string[] GetPropertyKeys(PropertyInfo propertyInfo)
        {
            if (propertyInfo == null)
                return new string[] { "public", "{", "get", "private", "set", "}", ";" };
            return new string[] { "public", "{", "get", "private", "set", "}", ";", propertyInfo.PropertyType.Name, propertyInfo.Name };
        }

        /// <summary>
        /// InitComponent 行
        /// </summary>
        /// <returns></returns>
        private string[] GetInitMethodKeys()
        {
            return new string[] { "private", "void", "InitComponent", "()" };
        }

        /// <summary>
        /// ClearComponent 行
        /// </summary>
        /// <returns></returns>
        private string[] GetClearMethodKeys()
        {
            return new string[] { "private", "void", "ClearComponent", "()" };
        }

        #endregion

        // 创建 CodeNamespace
        private void CreateNamespace(CodeCompileUnit codeCompileUnit)
        {
            for (int i = 0; i < nameSpacePL.Count; i++)
            {
                string name = nameSpacePL[i].head.script.Replace("namespace", "").Trim();
                CodeNamespace nameSpace = new CodeNamespace(name);
                codeCompileUnit.Namespaces.Add(nameSpace);
                for (int j = 0; j < usingPL.Count; j++)
                {
                    string usingName = nameSpacePL[i].head.script.Replace("Using", "").Replace(";","").Trim();
                    nameSpace.Imports.Add(new CodeNamespaceImport(usingName));
                }
            }
        }

        // 创建 CodeTypeDeclaration
        private void CreateClass(CodeNamespace codeNamespace)
        {
            for (int i = 0; i < classPL.Count; i++)
            {
                string name = classPL[i].head.script.
                    Split(new string[] { "class"}, StringSplitOptions.RemoveEmptyEntries)[1].
                    Split(':')[0].
                    Trim();
                CodeTypeDeclaration codeType = new CodeTypeDeclaration(name);
                CodeTypeReference codeTypeReference = new CodeTypeReference(typeof(PanelBase));
                codeType.BaseTypes.Add(codeTypeReference);
                codeNamespace.Types.Add(codeType);
            }
        }

        // 创建 CodeMemberMethod
        private void CreateMethod(CodeTypeDeclaration codeTypeDeclaration)
        {
            // TODO：仅支持无返回值和private访问级别
            for (int i = 0; i < methodPL.Count; i++)
            {
                // 剔除关键字
                string name = RealaceKeyWord(methodPL[i].head.script);
                // 剔除括号和返回值
                name = name.Split('(')[0].Split(' ')[1].Trim();
                CodeMemberMethod codeMemberMethod = new CodeMemberMethod();
                codeMemberMethod.Attributes = MemberAttributes.Private;
                codeMemberMethod.Name = name;
                codeTypeDeclaration.Members.Add(codeMemberMethod);
            }
        }

        // source 中是否包含 items 中的所有元素
        private bool ContainsAllStrItem(string source, string[] items)
        {
            if (items == null)
                return false;
            for (int i = 0; i < items.Length; i++)
            {
                string item = items[i];
                if (!source.Contains(item))
                    return false;
            }
            return true;
        }

        // str 在代码中是否是字符串的一部分
        private bool IsString(string script, string str)
        {
            if (!script.Contains(str)) return false;
            int index = script.IndexOf(str);
            int i0 = script.IndexOf("\"");
            int i1 = script.LastIndexOf("\"");
            return (index < i0 && index < i1);
        }

        // str 在代码中是否是注释的一部分
        private bool IsAnnotation(string script, string str)
        {
            if (!script.Contains(str)) return false;
            int index = script.IndexOf(str);
            int i0 = script.IndexOf("//");
            int i1 = script.IndexOf("/*");
            return (i0 >= 0 && i0 < index) || (i1 >= 0 && i1 < index);
        }

        // 是否是方法体的一部分
        private bool IsMethodBody(int index)
        {
            for (int i = 0; i < methodPL.Count; i++)
                if (index < methodPL[i].body.end && index > methodPL[i].body.start)
                    return true;
            return false;
        }

        // 剔除关键字
        private string RealaceKeyWord(string value)
        {
            return value.Replace("abstract", "").
                   Replace("void", "").
                   Replace("virtual", "").
                   Replace("override", "").
                   Replace("private", "").
                   Replace("public", "").
                   Replace("protected", "").
                   Replace("internal", "").
                   Replace("override", "").
                   Replace("static", "").
                   Replace("const", "").
                   Replace("new", "");
        }

        private struct Code
        {
            //public int bodyStart;
            //public int bodyEnd;

            //public int headStart;
            //public int headEnd;

            public Block head;
            public Block body;

            public Code(Block head, Block body)
            {
                this.head = head;
                this.body = body;
            }
        }

        private struct Block
        {
            public int start;
            public int end;
            public string script;
        }
    }
}